home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Ham Radio 2000
/
Ham Radio 2000.iso
/
ham2000
/
packet
/
p_tapr
/
tnchst
/
blp.c
< prev
next >
Wrap
Text File
|
1992-03-16
|
22KB
|
832 lines
/***** BINARY LINK PROTOCOL SOFTWARE (c) 1990-91 Howard Goldstein N2WX***/
/* all rights reserved. permission for non-commercial amateur use
/* granted */
#include "blp.inc"
/*** global variables ***/
/* storage for blp control blocks */
struct blpcb_struct blp_cb[NLINKS];
void cdecl blp_init() /* call on init */
{
int x;
for ( x = 0 ; x < NLINKS ; x++ ) /* set up all of em */
{
blp_cb[x].bsstate = BSIDLE ; /* bsstate to idle */
blp_cb[x].timer = (-1) ; /* stop timer */
}
}
/* set timer to expired and zero the retry counter - called on
state change */
void cdecl blp_timer_retry_reset( blpcb )
struct blpcb_struct *blpcb;
{
blpcb->timer = 0 ; /* causes immediate expiration */
blpcb->retry_count = 0 ;
}
void cdecl blp_event( event, blpcb, bframe, len)
byte event; /* as defined by preproc directives */
struct blpcb_struct * blpcb;
struct bframe_struct * bframe; /* ptr to frame, if any associated w/ event */
word len; /* len " " */
{
byte nstate;
/* these are the state tables; included later, must
be identified here */
extern struct state_entry blps_table[][NSEVENTS],
blpd_table[][NDEVENTS];
void (*hdlr) (struct blpcb_struct *, struct bframe_struct *,
word );
/* supervisory event? */
if ( event < FIRST_DATA_EVENT )
{
/* set up local vars w/ results of lookup */
nstate = blps_table[blpcb->bsstate - 1][event].newstate ;
hdlr = blps_table[blpcb->bsstate - 1][event].action_handler ;
#if BLP_EVENT_TRACE
/* display events */
printf(" Chan=%d SEvent=%d Sstate=%d Dstate=%d Newstate=%d hdlr=%x\n",
blpcb->local_chan, event,
blpcb->bsstate,blpcb->bdstate, nstate, hdlr );
#endif
/* if new state is mentioned and diff from the
current state */
if ( nstate && nstate != blpcb->bsstate)
{
blp_timer_retry_reset( blpcb ); /* reset timer, try */
blpcb->bsstate = nstate ; /* change state */
}
if ( hdlr ) /* if a handler was defined */
(*hdlr) (blpcb,bframe,len) ; /* then run it */
/** note assumption in collission handler
and loopback requriing nstate change be
done prior to handler execution, as above **/
}
else /* data event */
{
/* must be in supvy data state to operate
on data events */
if ( blpcb->bsstate == BSDATA ) /* if in data state */
{
event -= FIRST_DATA_EVENT ; /* take bias out - start
counting at 0 */
/** note quite similar to above; may want to compress later **/
nstate = blpd_table[blpcb->bdstate - 1][event].newstate ;
hdlr = blpd_table[blpcb->bdstate - 1][event].action_handler ;
#if BLP_EVENT_TRACE
/* display events */
printf(" Chan=%d DEvent=%d Sstate=%d Dstate=%d Newstate=%d hdlr=%x\n",
blpcb->local_chan, event,
blpcb->bsstate,blpcb->bdstate, nstate, hdlr );
#endif
/* if new state is mentioned and diff from the
current state */
if ( nstate && nstate != blpcb->bdstate)
{
blp_timer_retry_reset( blpcb ); /* reset timer, try */
blpcb->bdstate = nstate ; /* change state */
}
if ( hdlr ) /* if a handler was defined */
(*hdlr) (blpcb,bframe,len) ; /* then run it */
} /* endif in data state */
} /* endif data event */
}
/* call on timer tick intervals - updates and signals expiration
events */
void cdecl blp_timer_tick()
{
byte x;
for ( x = 0 ; x < NLINKS ; x++ ) /* traverse all blps */
{
if ( blp_cb[x].bsstate != BSIDLE )
blp_event( LUNBUSY, &blp_cb[x], NULL, 0);
if ( blp_cb[x].bsstate != BSIDLE /* if not idle and */
&& blp_cb[x].timer >= 0 ) /* timer not stopped */
{ /* then count time pd - */
if ( ( --blp_cb[x].timer < 1 ) ) /* if now expired */
{ /* then signal event*/
/* force timer to stay expired */
--blp_cb[x].timer ;
/* count retry */
if ( blp_cb[x].retry_count++ == BLP_MAX_TRIES )
/* if retry lim excdd then LRETRY event*/
blp_event( LRETRY, &blp_cb[x], NULL, 0 );
else
/* else TIMER event, */
/* type event depends on
whether in supvy state or
not */
blp_event(
blp_cb[x].bsstate == BSDATA ? LDTIMER : LSTIMER ,
&blp_cb[x], NULL, 0 );
}
}
}
}
/* initialize blpcb data machine, ptrs etc */
void cdecl blp_data_init( blpcb )
struct blpcb_struct *blpcb;
{
blpcb->bdtxseq = blpcb->bdrxseq = blpcb->outstanding = 0 ;
blpcb->txpackets.next = NULL ; /* clr linked list ptr */
blpcb->bdstate = BDIDLE ; /* state -> IDLE */
}
void cdecl send_blp_fr( blpcb, blp_cmd, info, len)
struct blpcb_struct *blpcb;
byte blp_cmd ; /* cmd byte */
byte *info;
word len;
{
byte *insertptr ; /* ptr to insert data */
byte wkarea[AFTMAXFRAMESIZE]; /* work area to insert at */
int newlen;
newlen = len+2 ; /* length is size of data +
size of header */
insertptr = wkarea ; /* pointer we'll move data to */
#if LOOPBACK
*insertptr++ = blpcb->lcn ^ 0xC0; /* stick lcn in aft data fld */
#else
*insertptr++ = blpcb->lcn ; /* stick lcn in aft data fld */
#endif
*insertptr++ = blp_cmd ; /* and stick cmd byte too */
memcpy( insertptr, info, len ) ; /* move data */
dlc_data_cmd( wkarea, newlen ); /* give it to dlc */
}
int cdecl send_blp_datagram( lcn , info, len)
byte lcn;
byte *info;
word len;
{
byte *insertptr ; /* ptr to insert data */
byte wkarea[AFTMAXFRAMESIZE]; /* work area to insert at */
int newlen;
newlen = len+2 ; /* length is size of data +
size of header */
insertptr = wkarea ; /* pointer we'll move data to */
*insertptr++ = lcn ; /* stick lcn in aft data fld */
*insertptr++ = UDATA ; /* and stick cmd byte too */
memcpy( insertptr, info, len ) ; /* move data */
dlc_data_cmd( wkarea, newlen ); /* give it to dlc */
return( TRUE ); /* force OK return status */
}
/* try to find blpcb for this lcn. return *blpcb or NULL */
struct blpcb_struct * cdecl find_lcn( lcn )
byte lcn;
{
int x;
for ( x = 0 ; x < NLINKS ; x++ )
/* if found, then break now */
if ( blp_cb[x].lcn == lcn && blp_cb[x].bsstate != BSIDLE )
break ;
/* if x >= NLINKS then not found */
if ( x >= NLINKS )
return (NULL) ; /* not found */
else
return (&blp_cb[x]) ; /* else ptr to it */
}
void cdecl send_cclrd(blpcb, bframe, len)
struct blpcb_struct *blpcb;
struct bframe_struct *bframe;
word len;
{
send_blp_fr( blpcb, CCLRD, NULL, 0);
}
#if FALSE
void cdecl send_ca(blpcb, bframe, len)
struct blpcb_struct *blpcb;
struct bframe_struct *bframe;
word len;
{
send_blp_fr( blpcb, 0x03, NULL, 0);
}
void cdecl send_cca(blpcb,bframe,len)
struct blpcb_struct *blpcb;
struct bframe_struct *bframe;
word len;
{
send_blp_fr( blpcb, 0x05, NULL, 0);
}
void cdecl send_dnetack(blpcb,bframe,len)
struct blpcb_struct *blpcb;
struct bframe_struct *bframe;
word len;
{
/* send_blp_fr( blpcb, 0xF0 + blpcb->bdrxseq, NULL, 0); */
}
#endif
void cdecl send_dack(blpcb,bframe,len)
struct blpcb_struct *blpcb;
struct bframe_struct *bframe;
word len;
{
send_blp_fr( blpcb, DACK + blpcb->bdrxseq, NULL, 0);
}
void cdecl send_dbusy(blpcb,bframe,len)
struct blpcb_struct *blpcb;
struct bframe_struct *bframe;
word len;
{
send_blp_fr( blpcb, DBUSY + blpcb->bdrxseq, NULL, 0);
}
void cdecl send_cstrep(blpcb,bframe,len)
struct blpcb_struct *blpcb;
struct bframe_struct *bframe;
word len;
{
auto byte dd[2] ; /* store status values here */
dd[0] = blpcb->bsstate; dd[1] = blpcb->bdstate; /* store states */
send_blp_fr( blpcb, CSTREP , dd, 2); /* send the packet */
}
void cdecl send_cstenq(blpcb,bframe,len)
struct blpcb_struct *blpcb;
struct bframe_struct *bframe;
word len;
{
send_blp_fr( blpcb, CSTENQ , NULL , 0);
}
void cdecl ring_in(blpcb, bframe, len)
struct blpcb_struct *blpcb;
struct bframe_struct *bframe;
word len;
{
int x;
struct itm_struct *itm;
/* need to assign blpcb cause the entered one is no good */
/* find unused blpcb */
for ( x = 0 ; x < NLINKS ; x++ )
if ( blp_cb[x].bsstate == BSIDLE ) /* if unused */
break; /* then break */
/* if couldnt find an empty blpcb */
if ( x == NLINKS )
{
/* then send call clr command */
/* if able to allocate intertask message for
one-shot call clear cmd */
if ( itm= (struct itm_struct *)
malloc(sizeof(struct itm_struct) + AFTMAXFRAMESIZE))
{
itm->itm_data[0] = bframe->lcn;
itm->itm_data[1] = 0x08 ; /* cclr command */
itm->msg_len = 2 ; /* two-bytes long */
aftt_send( itm ) ; /* send the frame */
free( (char *) itm );
}
else ; /* else couldnt allocate, so drop on floor */
}
else
{ /* else empty blpcb found -- blp_cb[x] is empty */
blp_cb[x].lcn = bframe->lcn; /* store lcn in incoming
blp_cb */
blp_data_init( &blp_cb[x] ) ; /* init data machine */
/* copy address */
strcpy( blp_cb[x].address,bframe->data);
/*** SPECIAL CASE :
since no blpcb was found in the event
handler, we need to force correct state
& do retry/reset, as if event handler
had done it from the table
***/
blp_cb[x].bsstate = BSLCSETUP;
blp_timer_retry_reset( &blp_cb[x] );
/* try to start upper level hander */
blp_cb[x].local_chan = blp_start_handler( &blp_cb[x] );
/** NOTE should do some checking here for
inability to open upper channel, no? **/
/**NOTE2 -- in non-queued event struct, equate
aboove to local chan is unecessary **/
/* send_ca( &blp_cb[x], NULL, 0 ); /* send call accepted */
}
}
void cdecl collide(blpcb, bframe, len)
struct blpcb_struct *blpcb;
struct bframe_struct *bframe;
word len;
{
#if LOOPBACK
/* these are defined in the handlers later on */
extern struct blpcb_struct *blp_intfc[];
extern byte nextchan;
/* loopback used for debug testing -
collissions construed to be loopbacks */
blpcb->bsstate = BSDATA ; /* force supv'y data state */
blp_connected_handler( blpcb->local_chan) ; /* tell upper
level of cnct */
blp_intfc[nextchan++] = blpcb; /* save ptr to blp cb */
blp_data_init( blpcb ) ; /* init data machine */
#else
/* else normal proc is to inform of disc and
let state table take down the link */
blp_stop_handler( blpcb->local_chan ) ; /* tell upper lev of
loss */
#endif
}
void cdecl answered(blpcb, bframe, len)
struct blpcb_struct *blpcb;
struct bframe_struct *bframe;
word len;
{
blp_connected_handler( blpcb->local_chan ) ; /* tell upper
level of cnct */
/* send_cca(blpcb,bframe,len); */
}
void cdecl caller_rdy(blpcb, bframe, len)
struct blpcb_struct *blpcb;
struct bframe_struct *bframe;
word len;
{
blp_connected_handler( blpcb->local_chan );
retrycc(blpcb,NULL,0);
}
void cdecl clr_reply(blpcb, bframe, len)
struct blpcb_struct *blpcb;
struct bframe_struct *bframe;
word len;
{
send_cclrd( blpcb, bframe, len );
blp_stop_handler( blpcb->local_chan );
}
void cdecl stopblp(blpcb, bframe, len)
struct blpcb_struct *blpcb;
struct bframe_struct *bframe;
word len;
{
struct datapacket_struct *ptr; /* use to free
all outstanding pkts */
ptr = blpcb->txpackets.next ; /* 1st packet, if any */
while ( ptr )
{
free( (char *) ptr ) ; /* free the buffer*/
ptr = ptr->next ; /* and go to next one */
}
blpcb->txpackets.next = NULL ; /* leave it null */
blp_stop_handler( blpcb->local_chan );
}
void cdecl retryfail(blpcb, bframe, len)
struct blpcb_struct *blpcb;
struct bframe_struct *bframe;
word len;
{
blp_stop_handler( blpcb->local_chan );
}
void cdecl retrycs(blpcb, bframe, len)
struct blpcb_struct *blpcb;
struct bframe_struct *bframe;
word len;
{
int x;
byte *lastdr;
lastdr = blpcb->address;
x = 0;
while ( *lastdr ) { lastdr++; x++ ;}
if ( blpcb->email )
send_blp_fr( blpcb, CSEMAIL, blpcb->address, x );
else
send_blp_fr( blpcb, CS, blpcb->address, x );
/* blpcb->timer = BLP_RETRY_TIME ; /* */
}
void cdecl retrycc(blpcb, bframe, len)
struct blpcb_struct *blpcb;
struct bframe_struct *bframe;
word len;
{
send_blp_fr( blpcb, CCC, NULL, 0);
/* blpcb->timer = BLP_RETRY_TIME ; /* */
}
void cdecl retrycclr(blpcb, bframe, len)
struct blpcb_struct *blpcb;
struct bframe_struct *bframe;
word len;
{
send_blp_fr( blpcb, CCLR, NULL, 0);
/* blpcb->timer = BLP_RETRY_TIME ; /* */
}
void cdecl senddata(blpcb, bframe, len)
struct blpcb_struct *blpcb;
struct bframe_struct *bframe;
word len;
{
byte ts; /* transmit sequence # holder */
struct datapacket_struct *pkt ; /* point to data to send */
ts = blpcb->bdtxseq ; /* starting tx seq # */
pkt = &blpcb->txpackets ; /* points to oldest outstand
ing packet, if !null */
/* now traverse the linked list of pktzd xmit data */
while ( pkt->next ) /* while pkt in the list */
{
pkt = pkt->next ; /* traverse to next pkt */
/* send the packet */
send_blp_fr( blpcb, DDATA + ts, pkt->data, pkt->len );
ts = ( ( ts+1 ) & 15 ); /* next seq #, mod 16 */
}
/*endwhile */
blpcb->timer = BLP_RETRY_TIME ; /* restart timer */
}
void cdecl dataproc(blpcb, bframe, len)
struct blpcb_struct *blpcb;
struct bframe_struct *bframe;
word len;
{
if ( (bframe->cmd & 15) == blpcb->bdrxseq ) /* if seq # matches */
{
if ( blp_data_handler( blpcb->local_chan, bframe->data,
len-2 ) ) /* if upper level
toook the frame */
{
/* mod16 bump*/
blpcb->bdrxseq = ( (blpcb->bdrxseq+1) & 15 );
send_dack( blpcb, NULL, 0 ); /*then send ack, */
}
else /* upper level could not take the frame */
blp_event( LDATABUSY, blpcb, NULL, 0); /* so sig busy event */
}
else /* else seq # didnt match */
send_dack( blpcb, NULL,0 ); /* send what we do hv */
}
void cdecl ackproc(blpcb, bframe, len)
struct blpcb_struct *blpcb;
struct bframe_struct *bframe;
word len;
{
byte ackseq, nacked; /* remote acks to seq ackseq, nacked
# of packets */
struct datapacket_struct *ptr; /* use to delete packets from
head of linked list */
blpcb->retry_count = 0 ; /* reset retry count */
ackseq = ( bframe->cmd & 15 ) ; /* strip off seq #, mod 16 */
nacked = ( ackseq - blpcb->bdtxseq ) & 15 ; /* he acks this many */
if ( nacked > blpcb->outstanding ) /* if too many */
printf("*** error- too many acked\n"); /* need to do error!*/
else /* else no problem */
{
if ( blpcb->outstanding > 0 ) /* if packets were outsdng */
{
/* if ( blpcb->bdtxseq == blpcb->bdrxseq ) /** if all acked WHEN 1 PACKET */
if ( (blpcb->outstanding-=nacked) == 0 ) /* if all ackd */
blp_event( LBLPACK, blpcb, NULL, 0) ; /* then gen event */
blpcb->bdtxseq = ackseq ; /* update tx seq # */
while ( nacked-- ) /* while packets left to release */
{
ptr = blpcb->txpackets.next->next ;
/* link to 2d packet */
free( (char *) blpcb->txpackets.next ); /* free 1st packet */
blpcb->txpackets.next = ptr ; /* make 2d pkt the 1st in the
head record */
} /*endwhile packets left to release*/
} /*endif outstanding packets */
} /*endif else */
}
/** called from underlying DLC receiver **/
/** converts DLC frames into events and runs the event handler **/
void cdecl dlc_in_handler( bframe, len )
struct bframe_struct *bframe;
word len;
{
byte event ; /* will convert blp cmd to event */
int x ;
struct blpcb_struct *blpcb ;
/* try to find the blpcb for this lcn */
blpcb = find_lcn( bframe->lcn );
if ( !blpcb ) /* if not connected */
switch( bframe->cmd ) /* then what kind of cmd? */
{
case CS:
/* kluge to get around need
to know blpcb in event tables */
ring_in( NULL, bframe, len );
break ;
case CCLR:
/* will need to respond here */
break ;
case UDATA: /* uncnctd data */
break ; /* ignore for now */
default:
break; /* ignore others */
}
else
{
switch ( bframe->cmd ) /* on command, */
{
case CS: event = RCVD_CS; break;
/* case 0x03: event = RCVD_CA; break; */
case CCC: event = RCVD_CCC; break;
/* case 0x05: event = RCVD_CCCACK; break; */
case CCLR: event = RCVD_CCLR; break;
case CCLRD: event = RCVD_CCLRD; break;
case CSTENQ: event = LRXCSTENQ; break;
case CSTREP: event = LDTIMER; break; /** fake LDTIMER xpire -
speeds up BLP */
default:
/* check for data states w/ variable lwr nibble*/
switch( bframe->cmd & 0xF0 ) /* strip of seq #*/
{
case DDATA: event = LRXDATA; break;
case DACK:
case DBUSY: event = LRXACK; break;
default:
/* unknown cmd - err */
printf("$%02X unknown cmd\n",bframe->cmd);
return;
break;
}
break;
}
/* dispatch event only when blpcb is known */
blp_event( event, blpcb, bframe, len );
} /* endif */
}
/*************************************************************
Interface : transport>blp
**************************************************************/
/** initiate a blp link; transport ref is chan # chan,
enpoint is at string @ address
returns ptr to blpcb structure if able to initiate,
else returns NULL if error **/
/*** start -- enter address[0] == '!' if call to email port desired ***/
struct blpcb_struct * cdecl
blp_start_cmd( chan, address )
byte chan; /* transport ref*/
char *address; /* string w/ address */
{
byte lcn; /* will need to select an lcn */
int x;
strupr( address ); /* convert address to uppper case */
/* 'cause will have to look for tnc cmd setup*/
if ( ! strncmp( address, "TNCCMD", 6 ) ) /* if cmd port addrsd */
lcn = 0x70 ; /* then select cmd port */
else
/* find an unused lcn */
for ( lcn = 0 ; lcn < 0x6e ; lcn++ )
if ( ! find_lcn( lcn ) )
break ; /* break if unused found */
if ( lcn == 0x6f ) /* if no lcn found */
{
return ( NULL ); /* then return err */
}
else
{
/* find empty blpcb */
for ( x = 0 ; x < NLINKS ; x++ )
if ( blp_cb[x].bsstate == BSIDLE )
{
/*** if email port, set flag and fix addr ****/
if ( address[0] == '!' )
{
strcpy( address, address+1); /* fix address, */
blp_cb[x].email = TRUE ; /* set email flag */
}
else blp_cb[x].email = FALSE; /* else clear it */
/* if empty blpcb found then */
/* put called address in blpcb */
atocall( blp_cb[x].address, address ) ;
/** force one callsign only **/
/* blp_cb[x].address[6] |= 1 ; */
blp_cb[x].address[7] = 0 ;
blp_data_init( &blp_cb[x] ) ;
/* init data machine */
blp_cb[x].local_chan = chan ;
blp_cb[x].lcn = lcn ;
blp_event( LSTART, &blp_cb[x], NULL, 0);
return ( &blp_cb[x] );
}
/* if fell through here then couldnt find an empty blpcb;
return NULL for error */
return (NULL);
}
}
/* send data on a particular link. returns TRUE if
sent ok */
int cdecl blp_data_cmd( blpcb, data, len )
struct blpcb_struct *blpcb;
byte *data;
word len;
{
/* vars needed to build and add to linked list */
struct datapacket_struct *newpacket, *ptr;
if ( blpcb->outstanding < BLP_MAX_OUTSTANDING /*if able to pktz more */
&& blpcb->bsstate == BSDATA /* and in a data state */
&& ( newpacket = (struct datapacket_struct *)
malloc(sizeof(struct datapacket_struct)) ) )
/* and able to allocate then */
{
newpacket->len = len ; /* set up len field */
memcpy(newpacket->data,data,len); /* set up data */
newpacket->next = NULL ; /* init link field */
/* now add to tail of linked list */
ptr = &blpcb->txpackets ; /*1st, ptt to hdr dummy struct */
while ( ptr->next ) /* while not at end of list */
ptr = ptr->next ; /* traverse */
/*now merely put ptr to new packet at end of list link*/
ptr->next = newpacket;
blpcb->outstanding++;
/* signal new data */
blp_event( LNEWDATA, blpcb, NULL, 0);
return( TRUE );
} else return (FALSE);
}
void blp_stop_cmd( blpcb )
struct blpcb_struct *blpcb;
{
blp_event( LSTOP, blpcb, NULL, 0 );
}
void blp_status_cmd( blpcb )
struct blpcb_struct *blpcb;
{
send_cstenq( blpcb, NULL, 0);
}
/*************************************************************
Interface : blp>transport
**************************************************************/
/* variable - hold blpcb pointers for each channel */
struct blpcb_struct *blp_intfc[NLINKS+1] ;
byte nextchan = 0 ; /* just assign local chans in order */
byte cdecl blp_start_handler( blpcb )
struct blpcb_struct *blpcb;
{
char temp[80]; /* address temporarily built here */
calltoa( temp, blpcb->address); /* convert address to ascii */
printf("\nCall for %s accepted on channel %d", temp, nextchan );
blp_intfc[nextchan] = blpcb; /* save ptr to blp cb */
blpcb->local_chan = nextchan; /* NEED to set this here since
events are not queued &
next transition requires it
to be */
/*** Call for connected immediately since it's local */
blp_event( LCNCTD, blpcb, NULL, 0 ); /* generate the event */
return( nextchan++ ) ; /* return chan # (& bump it) */
}
void cdecl blp_connected_handler( chan )
byte chan;
{
printf("\nConnected on channel %d", chan );
}
void cdecl blp_stop_handler( chan )
byte chan;
{
printf(" Channel %d stopped\n", chan);
}
/*** should return TRUE if able to accept data ***/
int cdecl blp_data_handler( chan, data, len )
byte chan, *data;
word len;
{
printf("\nData on chan %d:",chan);
while ( len-- )
putchar(*data++);
putchar('\n');
return( TRUE ); /* always return true in this intfc */
}
/* get state tables */
#include "blptables.c"